package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.api.IApUserClientFeign;
import com.heima.comment.dtos.ApCommentVo;
import com.heima.comment.dtos.CommentLikeDto;
import com.heima.comment.dtos.CommentListDto;
import com.heima.comment.dtos.CommentSaveDto;
import com.heima.comment.pojos.ApComment;
import com.heima.comment.pojos.ApCommentLike;
import com.heima.comment.service.CommentService;
import com.heima.common.constants.HotArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.message.vos.UpdateArticleMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.threadLocal.ApUserThreadLocalUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CommentServiceImpl implements CommentService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IApUserClientFeign apUserClientFeign;

    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 发表评论
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveComment(CommentSaveDto dto) {

        //1.参数校验
        if(dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前文章id不存在");
        }
        if(dto.getContent()==null || dto.getContent().equals(" ") || dto.getContent().length()>140){
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"发表的内容出现了问题");
        }

        //评论内容审核


        //1.5 获取用户信息
        ApUser apUser = ApUserThreadLocalUtil.getApUser();
        if(apUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer userId = apUser.getId();
        if(userId==0){
            //2.1 如果是游客，则不可以执行以下操作，终止
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH,"游客身份，没有权限发表评论");
        }

        //2.查询redis,如果redis中有值，直接使用用户信息，如果没有值，则远程调用fegin,同时也要缓存一份数据到redis
        String key="LOGIN_USER_"+userId;
        String userStr = redisTemplate.opsForValue().get(key);
        if(userStr==null){//表示不存在
            //远程调用fegin
            apUser = apUserClientFeign.findById(userId);
            //保存数据到redis
            redisTemplate.opsForValue().set(key, JSON.toJSONString(apUser),2, TimeUnit.HOURS);
        }else{//表示存在
            apUser = JSON.parseObject(userStr, ApUser.class);
        }
        //3.保存数据到mongo中
        ApComment apComment=new ApComment();
        apComment.setLikes(0);//点赞数
        apComment.setUserId(apUser.getId());
        apComment.setCreatedTime(new Date());
        apComment.setImage(apUser.getImage());
        apComment.setContent(dto.getContent());
        apComment.setNickName(apUser.getName());
        apComment.setReply(0);//回复数
        apComment.setUpdatedTime(new Date());
        apComment.setTargetId(String.valueOf(dto.getArticleId()));
        mongoTemplate.save(apComment);


        //发送消息，处理流式计算
        UpdateArticleMess mess=new UpdateArticleMess();
        mess.setArticleId(dto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.COMMENT);
        mess.setAdd(1);
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));

        //4.返回结果
        return ResponseResult.okResult("发表评论完成");
    }

    /**
     * 点赞评论
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult likeComment(CommentLikeDto dto) {
        //1、参数校验
        if(dto.getCommentId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前评论id为空");
        }
        //2。判断当前是否是登录用户还是游客身份
        ApUser apUser = ApUserThreadLocalUtil.getApUser();
        if(apUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer userId = apUser.getId();
        if(userId==0){
            //2.1 如果是游客，则不可以执行以下操作，终止
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH,"游客身份，没有权限点赞评论");
        }
        //3.查询当前评论信息是否存在
        ApComment apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        if(apComment==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"没有查询到评论信息");
        }
        String key="COMMENT_LIKE_"+userId;
        //4.当前如果已经点赞过，且正在执行的是点赞动作，则终止
        Boolean flag = redisTemplate.opsForSet().isMember(key, dto.getCommentId());
        if(flag && dto.getOperation()==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST,"当前用户已经点赞过该评论");
        }

        //5.当前如果已经点赞过，且正在执行的是取消点赞动作，则删除redis和mongo（删除点赞集合，修改评论集合中的点赞数量-1）
        if(flag && dto.getOperation()==1){
            redisTemplate.opsForSet().remove(key,dto.getCommentId());

            //删除点赞集合
            mongoTemplate.remove(Query.query(
                    Criteria.where("userId").is(userId).and("targetId").is(dto.getCommentId())
            ), ApCommentLike.class);
            //修改评论集合数量-1
            Query query=Query.query(Criteria.where("userId").is(userId).and("id").is(dto.getCommentId()));
            /**
             * inc(String key, Number inc) 表示自增的api，保证了数据不会小于0
             * 参数1表示具体执行的哪个字段，
             * 参数2表示自增的数量，如果自增的话，数量值为整数，如果是自减的话，数量值可以是负数
             */
            UpdateDefinition update=new Update().inc("likes",-1);
            mongoTemplate.updateFirst(query,update,ApComment.class);
        }
        //6.当前如果没有点赞过，且正在执行的是点赞动作，则新增redis和mongo（新增点赞集合，修改评论集合中的点赞数量+1）
        if(!flag && dto.getOperation()==0){
           redisTemplate.opsForSet().add(key,dto.getCommentId());

            ApCommentLike apCommentLike=new ApCommentLike();
            apCommentLike.setUserId(userId);
            apCommentLike.setCreatedTime(new Date());
            apCommentLike.setTargetId(dto.getCommentId());
            mongoTemplate.insert(apCommentLike);

            Query query=Query.query(Criteria.where("userId").is(userId).and("id").is(dto.getCommentId()));
            /**
             * inc(String key, Number inc) 表示自增的api，保证了数据不会小于0
             * 参数1表示具体执行的哪个字段，
             * 参数2表示自增的数量，如果自增的话，数量值为整数，如果是自减的话，数量值可以是负数
             */
            UpdateDefinition update=new Update().inc("likes",1);
            mongoTemplate.updateFirst(query,update,ApComment.class);
        }
        //7.当前如果没有点赞过，且正在执行的是取消点赞动作，则终止
        if(!flag && dto.getOperation()==1){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST,"当前用户已经取消点赞执行过了");
        }
        //8.返回数据
        //一定要再次查询一下mongo，评论表
        ApComment byId = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        Map map=new HashMap();
        map.put("likes",byId.getLikes());


        return ResponseResult.okResult(map);
    }

    /**
     * 加载评论列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult listComment(CommentListDto dto) {

        //1.参数校验
        if (dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getSize() == null || dto.getSize() == 0) {
            dto.setSize(10);
        }

        //2.根据文章id查询评论集合列表
        Query query=Query.query(
                Criteria.where("targetId").is(dto.getArticleId().toString())
        ).
                //根据时间倒叙展示
                with(Sort.by(Sort.Direction.DESC,"updatedTime")).
                        //每页展示几条数据
                        limit(dto.getSize());
        List<ApComment> apCommentList = mongoTemplate.find(query, ApComment.class);

        //3.如果是游客身份，直接返回列表
        ApUser apUser = ApUserThreadLocalUtil.getApUser();
        if(apUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer userId = apUser.getId();
        if(userId==0){
            //如果是游客，只允许看到当前文章下的评论列表即可
           return ResponseResult.okResult(apCommentList);
        }

        //4.如果是登录用户的话，则要进行高亮
            //4.1 先根据用户id去查询当前用户是否点赞过某个评论，没有点赞过，直接返回apCommentList
        String key="COMMENT_LIKE_"+userId;
            //表示根据key获取到所有的value
        Set<String> members = redisTemplate.opsForSet().members(key);
        if(members==null){//说明当前用户并没有点赞过任何评论
            return ResponseResult.okResult(apCommentList);
        }
            //否则，表示点赞过，进行高亮设置
        if(apCommentList!=null && apCommentList.size()>0){
            List<ApCommentVo> apCommentVoList = apCommentList.stream().map(apComment -> {
                //目的是要从apComment对象转化成apCommentVo对象
                ApCommentVo vo = new ApCommentVo();
                BeanUtils.copyProperties(apComment, vo);
                //如果当前redis的集合中包含了该评论id
                if (members.contains(apComment.getId())) {
                    //则给operation=0表示点赞
                    vo.setOperation((short) 0);
                }
                return vo;
            }).collect(Collectors.toList());
            //5.返回数据
            return ResponseResult.okResult(apCommentVoList);
        }

        return null;
    }
}
